package main;

import java.applet.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.Area;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Random;

import javax.imageio.ImageIO;

import scenario.*;
import terrain.Terrain;
 
public class UI extends Applet
{
	public static final long serialVersionUID = 0;
	public static boolean DEBUG = false;
	public static Area DEBUGSHAPE = null;
	public Button debugButton;
	public static boolean buildingForOnline = false;

	public Game game;
	
	public PhaseUI phaseUI;
	public final MyKey myKey = new MyKey();
	
	public static Random rng = new Random();

	public Image logBackground = null;
	public int messageHeight = 130;
	public Image optionsBackground = null;
	public int optionsWidth = 225;
	public static ArrayList<String> messageLog = new ArrayList<String>();
	public int messageIndexBack = 0;
	public Button messageUp;
	public Button messageDown;
	
	public ArrayList<Component> panelComponents = new ArrayList<Component>();
	public ArrayList<ArrayList<Component>> storedPanels = new ArrayList<ArrayList<Component>>();
	
	public boolean drawingLine = false;
	public int rootX = 0;
	public int rootY = 0;
	public int destX = 0;
	public int destY = 0;
	
	public Unit selectedUnit = null;
	public Choice selectedChoice = null;
	public Button doneUsingMight;
	public ArrayList<ActionListener> mightListeners = new ArrayList<ActionListener>();
		
	public ArrayList<DieRoll> diceOnTable = new ArrayList<DieRoll>();
	
	public Unit targetedUnit = null;
	public Unit magicTarget = null;
	
	public Battle selectedBattle = null;
	
	public int boardOffsetX = 0;
	public int boardOffsetY = 0;
	
	public Panel statsPanel;
	public Button endPhase;
	public ArrayList<ArrayList<Component>> visibleComponentsQueue = new ArrayList<ArrayList<Component>>();
	public ArrayList<Component> permanentComponents = new ArrayList<Component>();
	
	public Graphics bufferG;
	public Image offscreen;
	public Dimension dim;
	
	public String message = "";
	
	public void init()
	{
		game = new BalinsTomb();
		
		/*ShootThread st = new ShootThread();
		ArrayList<String> testAL = new ArrayList<String>();
		st.testList = testAL;
		
		System.out.println(testAL.size());
		
		Thread t = new Thread(st);
		t.run();
		try {
			System.out.println("A");
			t.join();
			System.out.println("B");
		} catch (InterruptedException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}
		
		System.out.println(testAL.size());*/
		
		game.myUI = this;

		MyKey.myUI = this;
		//game.setupTest();
		
		setLayout(null);
		setSize(1100,750);
		dim = getSize();
		offscreen = createImage(dim.width, dim.height);
		bufferG = offscreen.getGraphics();
		
		if (!UI.buildingForOnline)		{
			File imFile = new File("../assets/woodBackground.jpg");
			try {
				this.logBackground = ImageIO.read(imFile);
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			imFile = new File("../assets/marbleBackground.jpg");
			try {
				this.optionsBackground = ImageIO.read(imFile);
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		/*else {
			this.logBackground = Utils.decodeToImage(ImageTexts.woodBackgroundStrs);
			this.optionsBackground = Utils.decodeToImage(ImageTexts.marbleBackgroundStrs);
		}*/

		UnitSelecter u = new UnitSelecter(this);
		phaseUI = u;
		u.beginPhase();

		repaint();
	}
	
	public void beginGame() {
		debugButton = new Button("Print info");
		debugButton.setBounds(dim.width-220,0,100,50);
		debugButton.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				if (selectedUnit != null) {
					selectedUnit.printInfo();
				}
			}
		});
		add(debugButton);
				
		endPhase = new Button("End Phase");
		endPhase.setName("End Phase");
		endPhase.setBounds(dim.width-100,0,100,50);

		final Label phaseLabel = new Label(game.currPhase + ": " + game.currArmy);
		phaseLabel.setBounds(dim.width/2-50,0,100,30);
		phaseLabel.setAlignment(Label.CENTER);
		
		
		endPhase.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				phaseUI.endPhase();
				phaseLabel.setText(game.currPhase + ": " + game.currArmy);
			}
		});
		
		messageUp = new Button("^");
		messageUp.setBounds(dim.width - optionsWidth - 25, dim.height-messageHeight, 25, 20);
		messageUp.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				messageDown.setEnabled(true);
				if (messageIndexBack + 10 < messageLog.size())
					messageIndexBack += 1;
				if (messageIndexBack + 10 >= messageLog.size())
					messageUp.setEnabled(false);
				repaint();
			}
		});
		add(messageUp);
		messageDown = new Button("v");
		messageDown.setBounds(dim.width - optionsWidth - 25, dim.height-20, 25, 20);
		messageDown.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				messageUp.setEnabled(true);
				if (messageIndexBack > 0)
					messageIndexBack -= 1;
				if (messageIndexBack == 0)
					messageDown.setEnabled(false);
				repaint();
			}
		});
		messageDown.setEnabled(false);
		add(messageDown);
		
		add(endPhase);
		add(phaseLabel);
		statsPanel = buildStatsPanel("Selected Unit", dim.width-100, dim.height-280);
		statsPanel.setName("statsPanel");
		add(statsPanel);
		
		selectedChoice = new Choice();
		selectedChoice.setLocation(dim.width-150, dim.height-300);
		for (Army a : game.armies){
			for (Unit u : a.units){ 
				selectedChoice.add(u.toChoiceString());
			}
		}
		ItemListener i = new ItemListener() {
			public void itemStateChanged(ItemEvent e) {
				String selectedName = selectedChoice.getSelectedItem();
				for (Army a : game.armies){
					for (Unit u : a.units){
						if (u.toChoiceString().equals(selectedName)) {
							selectedUnit = u;
						}
					}
				}
			}
		};
		selectedChoice.addItemListener(i);
		add(selectedChoice);
		
		doneUsingMight = new Button("Done Using Might");
		doneUsingMight.setBounds(dim.width-100, dim.height - 360, 100, 50);
		doneUsingMight.setVisible(false);
		add(doneUsingMight);
		
		
		this.permanentComponents.add(messageUp);
		this.permanentComponents.add(messageDown);
		this.permanentComponents.add(statsPanel);
		
		this.addKeyListener(myKey);
		
		phaseUI = new MoveUI();
		phaseUI.myUI = this;
		phaseUI.beginPhase();
		
		repaint();
	}
	
	public void enableMight(ActionListener mightDone) {
		doneUsingMight.setVisible(true);
		if (doneUsingMight.getActionListeners().length > 0) {
			for (ActionListener al : doneUsingMight.getActionListeners()) {
				mightListeners.add(al);
				doneUsingMight.removeActionListener(al);
			}
		}
		doneUsingMight.addActionListener(mightDone);
		repaint();
	}
	
	public void disableMight() {
		doneUsingMight.setVisible(false);
		for (ActionListener a : doneUsingMight.getActionListeners()) {
			doneUsingMight.removeActionListener(a);
		}
		if (mightListeners.size() > 0) {
			doneUsingMight.addActionListener(mightListeners.get(mightListeners.size() - 1));
			mightListeners.remove(mightListeners.size() - 1);
		}
	}
	
	public void selectUnit(Unit u){
		selectedUnit = u;
		selectedChoice.select(u.toChoiceString());
		phaseUI.selectUnit(u);
		revalidate();
		repaint();
	}
	
	public void refreshSelectChoice() {
		remove(selectedChoice);
		selectedChoice = new Choice();
		selectedChoice.setLocation(dim.width-150, dim.height-300);
		for (Army a : game.armies){
			for (Unit u : a.units){ 
				selectedChoice.add(u.toChoiceString());
			}
		}
		ItemListener i = new ItemListener() {
			public void itemStateChanged(ItemEvent e) {
				String selectedName = selectedChoice.getSelectedItem();
				for (Army a : game.armies){
					for (Unit u : a.units){
						if (u.toChoiceString().equals(selectedName)) {
							selectedUnit = u;
						}
					}
				}
			}
		};
		selectedChoice.addItemListener(i);
		add(selectedChoice);
		revalidate();
		repaint();
	}
	
	public void removeByName(String name) {
		for (int i = 0; i < this.getComponentCount(); i++) {
			if (this.getComponent(i).getName().equals(name)) {
				this.remove(i);
				i--;
			}
		}
	}
	public Component getByName(String name) {
		for (int i = 0; i < this.getComponentCount(); i++) {
			if (this.getComponent(i).getName().equals(name)) {
				return this.getComponent(i);
			}
		}
		return null;
	}
	
	
	public void storePanel() {
		for (Component c : panelComponents) {
			c.setVisible(false);
		}
		storedPanels.add(panelComponents);
		panelComponents = new ArrayList<Component>();
	}
	
	public void setPanel(ArrayList<Component> panel) {
		for (Component c : panel) {
			add(c);
		}
		panelComponents = panel;
	}
	
	public void restorePanel() {
		for (Component c : panelComponents) {
			remove(c);
		}
		panelComponents = storedPanels.remove(storedPanels.size()-1);
		for (Component c : panelComponents) {
			c.setVisible(true);
		} 
	}
	
	
	
	
	public void storeVisibleComponents() {
		ArrayList<Component> visibleComponents = new ArrayList<Component>();
		for (Component c : this.getComponents()) {
			if (c.isVisible() && !permanentComponents.contains(c)) {
				visibleComponents.add(c);
			}
		}
		visibleComponentsQueue.add(visibleComponents);
		// For debugging
		/*System.out.println("Storing: " + stringButtonList(visibleComponents));
		System.out.println("Queue is ");
		for (int i = 0; i < visibleComponentsQueue.size(); i++) {
			System.out.println(i + ": " + stringComponentList(visibleComponentsQueue.get(i)));
		}*/
	}
	
	public void disableComponents() {
		for (Component c : this.getComponents()) {
			if (c.isVisible() && !permanentComponents.contains(c)) {
				c.setVisible(false);
			}
		}
	}
	
	public void reenableComponents() {
		if (visibleComponentsQueue.size() != 0){
			ArrayList<Component> visibleComponents = visibleComponentsQueue.get(visibleComponentsQueue.size() - 1);
			visibleComponentsQueue.remove(visibleComponentsQueue.size() - 1);
			for (Component b : visibleComponents) {
				b.setVisible(true);
			}
		}
	}
	// Debug method for the 3 above
	public String stringComponentList(ArrayList<Component> componentList) {
		String s = "";
		for (Component b : componentList) {
			if (b instanceof Button)
				s += ((Button)b).getLabel() + ", ";
			else if (b instanceof Label)
				s += ((Label)b).getText() + ", ";
		}
		return s;
	}
	
	public Panel buildStatsPanel(String title, int x, int y) {
		Panel p = new Panel();
		Label t = new Label(title);
		Label moveLeft = new Label("Move Left: N/A");
		Label str = new Label("Strength: N/A");
		Label def = new Label("Defense: N/A");
		Label att = new Label("Attacks: N/A");
		Label wou = new Label("Wounds: N/A");
		Label cou = new Label("Courage: N/A");
		Label fv = new Label("FightValue: N/A");
		Label sv = new Label("ShootValue: N/A");
		Label mwf = new Label("Mt/Wl/Ft: N/A ");
		t.setSize(100, 10);
		t.revalidate();
		moveLeft.setSize(100, 10);
		str.setSize(100, 10);
		def.setSize(100, 10);
		
		p.setBounds(x, y, 100, 280);
		p.add(t);
		p.add(moveLeft);
		p.add(str);
		p.add(def);
		p.add(att);
		p.add(wou);
		p.add(cou);
		p.add(fv);
		p.add(sv);
		p.add(mwf);
		p.revalidate();
		return p;
	}
	
	public void updateStatsPanel() {
		if(statsPanel == null)
			return;
		if (selectedUnit != null) {
			((Label)statsPanel.getComponent(1)).setText("Move Left: " + String.format("%.2f", selectedUnit.moveLeft));
			((Label)statsPanel.getComponent(2)).setText("Strength: " + selectedUnit.getStrength());
			((Label)statsPanel.getComponent(3)).setText("Defense: " + selectedUnit.getDefense());
			((Label)statsPanel.getComponent(4)).setText("Attacks: " + selectedUnit.getAttacks());
			((Label)statsPanel.getComponent(5)).setText("Wounds: " + selectedUnit.getWounds());
			((Label)statsPanel.getComponent(6)).setText("Courage: " + selectedUnit.getCourage());
			((Label)statsPanel.getComponent(7)).setText("FightValue: " + selectedUnit.getFightValue());
			((Label)statsPanel.getComponent(8)).setText("ShootValue: " + selectedUnit.getShootValue());
			((Label)statsPanel.getComponent(9)).setText("Mt/Wl/Ft: " + selectedUnit.getMight() + "/" + 
															+selectedUnit.getWill()+"/" + selectedUnit.getFate());
		}
		else {
			((Label)statsPanel.getComponent(1)).setText("Move Left: -");
			((Label)statsPanel.getComponent(2)).setText("Strength: -");
			((Label)statsPanel.getComponent(3)).setText("Defense: -");
			((Label)statsPanel.getComponent(4)).setText("Attacks: -");
			((Label)statsPanel.getComponent(5)).setText("Wounds: -");
			((Label)statsPanel.getComponent(6)).setText("Courage: -");
			((Label)statsPanel.getComponent(7)).setText("FightValue: -");
			((Label)statsPanel.getComponent(8)).setText("ShootValue: -");
			((Label)statsPanel.getComponent(9)).setText("Mt/Wl/Ft: -/-/-");
		}
	
	}
	
	public void paint (Graphics g)
    {
		// draw things to bufferG
		this.requestFocus();
		
		if (game.armies[0] == null) {
			g.drawImage(offscreen, 0, 0, this);
			if(dim==null)
				dim = getSize();
			offscreen = createImage(dim.width, dim.height);
			bufferG = offscreen.getGraphics();
			return;
		}
		
		if (game.board.background != null) {
			bufferG.drawImage(game.board.background, -1*boardOffsetX, -1*boardOffsetY, null);
		}
		if (phaseUI instanceof UnitPlacer)
			phaseUI.paint(bufferG);
		
		// Draw terrain
		for(Terrain t : game.board.terrainList) {
			t.drawTo(bufferG);
		}
	
		// Draw units
    	for(Army a : game.armies) {
    		for (Unit u : a.units) {
        		bufferG.drawOval(u.x-u.pixelRadius-boardOffsetX, u.y-u.pixelRadius-boardOffsetY, 2*u.pixelRadius, 2*u.pixelRadius);
        		bufferG.drawString(u.name + "-" + u.id, u.x-boardOffsetX, u.y-boardOffsetY);
        		bufferG.drawString(u.equipmentString(), u.x-boardOffsetX, u.y+10-boardOffsetY);
    		}
    	}
    	
    	// Selected Unit/Battle stuff
    	if (selectedBattle != null && game.battles.contains(selectedBattle)) {
    		((Graphics2D)bufferG).setStroke(new BasicStroke(2));
    		bufferG.setColor(Color.CYAN);
    		for (Unit u : selectedBattle.goodUnits) 
    			bufferG.drawOval(u.x-(u.pixelRadius+2)-boardOffsetX, u.y-(u.pixelRadius+2)-boardOffsetY, 2*u.pixelRadius+4, 2*u.pixelRadius+4);
    		for (Unit u : selectedBattle.badUnits)
    			bufferG.drawOval(u.x-(u.pixelRadius+2)-boardOffsetX, u.y-(u.pixelRadius+2)-boardOffsetY, 2*u.pixelRadius+4, 2*u.pixelRadius+4);
    		bufferG.setColor(Color.BLACK);
    		((Graphics2D)bufferG).setStroke(new BasicStroke());
    	}
    	if (selectedUnit != null && selectedUnit.getWounds() > 0){
    		bufferG.drawOval(selectedUnit.x-(selectedUnit.pixelRadius+1)-boardOffsetX, selectedUnit.y-(selectedUnit.pixelRadius+1)-boardOffsetY, 2*selectedUnit.pixelRadius+2, 2*selectedUnit.pixelRadius+2);
    		if (selectedUnit.battle != null) {
    			bufferG.drawString(selectedUnit.battle.toString(), 150, dim.height-30);
    		}
    	}
    	
    	// Draw move line
    	if (drawingLine) {
    		bufferG.drawLine(rootX-boardOffsetX, rootY-boardOffsetY, destX-boardOffsetX, destY-boardOffsetY);
    		bufferG.drawString(String.format("%.2f", Utils.inchDist(rootX, rootY, destX, destY)), destX-boardOffsetX, destY-boardOffsetY);
    	}

    	
		updateStatsPanel();
		
		// Draw phase panel
		if(this.optionsBackground != null){
			bufferG.drawImage(this.optionsBackground, dim.width-225, 0, 225, dim.height, null);
		}
		else {
			bufferG.setColor(new Color(246,229,194)); // Lighter brown
			bufferG.fillRect(dim.width-225, 0, 225, dim.height);
			bufferG.setColor(Color.BLACK);
		}
		
		// Draw phase-specific things
		if (!(phaseUI instanceof UnitPlacer))
			phaseUI.paint(bufferG);
		
		// Draw message log
		if(this.logBackground != null){
			bufferG.drawImage(this.logBackground, 0, dim.height-messageHeight, dim.width-optionsWidth, messageHeight, null);
		}
		else {
			bufferG.setColor(new Color(80,50,2)); // Darker brown
			bufferG.fillRect(0, dim.height-messageHeight, dim.width-optionsWidth, messageHeight);
		}
		bufferG.setColor(Color.WHITE);
		Font oldFont = bufferG.getFont();
		bufferG.setFont(new Font("default", Font.BOLD, 14));
		bufferG.drawString("Message Log", 0, dim.height-messageHeight + 15);
		bufferG.setFont(oldFont);
		for (int i = messageIndexBack; i < messageIndexBack + Math.min(messageLog.size(), 10); i++){
			int locI = i - messageIndexBack;
			bufferG.drawString(messageLog.get(messageLog.size()-i-1), 0, dim.height-messageHeight + 30 + 10*(9-locI));
		}
		bufferG.setColor(Color.BLACK);
    	
    	
		
		// Draw debugshape if debugging is on
		if (DEBUG && DEBUGSHAPE != null) 
			((Graphics2D)bufferG).fill(DEBUGSHAPE);
		
		// Debugging battles - check every unit to see if they have a battle that's not in our games battle list, if so
		// spit out the state
		for(Army a : game.armies) {
    		for (Unit u : a.units) {
    			if (u.battle != null && !game.battles.contains(u.battle)) {
    				game.printInfo();
    				System.out.println(u.toChoiceString() + " has a battle not in the game's list");
    			}
    		}
    	}
    	
		g.drawImage(offscreen, 0, 0, this);
		offscreen = createImage(dim.width, dim.height);
		bufferG = offscreen.getGraphics();
    }
	
	public void update(Graphics g) 
    {
    	paint(g);
    }
	
	public static void main(String[] args) {
		UI toRun = new UI();
		
		javax.swing.JFrame window = new javax.swing.JFrame("Khazad-dum");
		window.setContentPane(toRun);
		window.setDefaultCloseOperation(javax.swing.JFrame.EXIT_ON_CLOSE);
		window.pack();
		window.setVisible(true);
		window.setSize(1115, 788);
		
		toRun.init();
	}

	public void endGame() {
		for (Component c : this.getComponents()) {
			c.setEnabled(false);
		}
		for (MouseListener ml : this.getMouseListeners()) {
			this.removeMouseListener(ml);
		}
	}
}